Dx.declare('Dx.code.parser.base_regex', {

   '@actions': {

      'register': [

         'validate',

         'process'

      ],

      'purge': [

         'cleanup'

      ]

   },

   str: '',

   hexRe: /^[0-9a-fA-F]+$/,

   numberRe: /^[0-9]+$/,

   literalEscapeSequence: {

      'b': "\b",     // backspace (U+0008 BACKSPACE)
      'f': "\f",     // form feed (U+000C FORM FEED)
      'n': "\n",     // line feed (U+000A LINE FEED)
      'r': "\r",     // carriage return (U+000D CARRIAGE RETURN)
      't': "\t",     // horizontal tab (U+0009 CHARACTER TABULATION)
      'v': "\v",     // vertical tab (U+000B LINE TABULATION)
      '0': "\0"      // null character (U+0000 NULL)

   },

   tokenNames: {

      '|': 'alt',

      '(': 'open_parenth',

      ')': 'close_parenth',

      '+': 'repeated',

      '*': 'repeated_or_none',

      '?': 'optional',

      '^': 'pattern_start',

      '[': 'charset_start',

      '[^': 'charset_start',

      ']': 'charset_end'

   },

   startTokens: {

      'char': true,

      'open_parenth': true,

      'pattern_start': true,

      'charset_start': true

   },

   endTokens: {

      'char': true,

      'repeated': true,

      'repeated_or_none': true,

      'optional': true,

      'close_parenth': true,

      'charset_end': true

   },

   _callback_filterTokenList: function(name, definition, list) {

      var names = this.names;

      if (list.hasOwnProperty(list)) {

         if (definition instanceof RegExp) {

            definition = definition.source;

         }

         // insert definition
         if (Dx.isString(definition, true)) {

            names[names.length] = name;

            this.items[name] = definition;

         }

      }

   },

   register_validate: function(o, name, str) {

      var D = Dx;

      if (str instanceof RegExp) {

         str = str.source;

         o.params[2] = str;

      }

      o.stop = !D.isString(name, true) || !D.isString(str, true);

   },

   register_process: function(o, name, str) {

      var chars = Array.prototype.slice.call(str, 0);

      var escapeSequence = this.literalEscapeSequence;

      var hexRe = this.hexRe;

      var numRe = this.numberRe;

      var tokenList = this.tokenNames;

      var starters = this.startTokens;

      var enders = this.endTokens;

      var S = String;

      var tokens = [];

      var tokenized = [];

      var tc = -1;

      var beforeToken = '';

      var beforeChar = '';

      var token, c, l, chr, next, ic, il, ichr;

      var escaped, escapelen, escapebase, re;

      var charSetMode = false;

      o.failed = false;

      o.tokens = tokens;

      for (c = 0, l = chars.length; l--;) {

         chr = chars[c++];

         processed = 0;

         // convert escaped to literal
         if (chr == '\\') {

            next = str.charAt(c);

            escaped = false;

            // hex
            if (next == 'x') {

               re = hexRe;

               escapelen = 3;

               escapebase = 16;

            // unicode
            } else if (next == 'u') {

               re = numRe;

               escapelen = 6;

               escapebase = 10;

            // octal
            } else if (numRe.test(str.substring(c, c + 2))) {

               re = numRe;

               escapelen = 3;

               escapebase = 8;

            // not escaped
            } else {

               escaped = false;

               // apply escape sequence
               if (next in escapeSequence) {

                  next = escapeSequence[next];

               }

            }

            if (escaped) {

               for (ic = c + 1, il = escapelen; il--;) {

                  ichr = chars[ic++];

                  if (!re.test(ichr)) {

                     ic--;

                     break;

                  }

               }

               chr = (ic - (c + 1)) ?

                     S.fromCharCode(

                        parseInt(

                           str.substring(c + 1, ic),

                           escapebase

                        )

                     ) : next;

               l -= ic - c;

               c = ic;

            // ordinary escaped char
            //    - apply escape sequence if registered
            } else {

               chr = next;

               c++;

            }

         // determine character set start (positive or negative)
         } else if (chr == '['){

            next = str.charAt(c);

            if (next == '^') {

               chr = '[^';

               c++;

            }

         // determine character set end
         } else if (chr == ']'){

            charSetMode = false;

         // set flag to disable character set concatenation
         } else if (beforeToken == 'charset_start') {

            charSetMode = true;

         }

         // parse token
         token = chr in tokenList ? tokenList[chr] : 'char';

         // apply concatenation if
         //    current token: char or open_parenth
         //    before token: close_parent or infix operator
         if (!charSetMode && token in starters && beforeToken in enders) {

            tokenized[tokenized.length] = '.';

            tokens[++tc] = ['concat', '.'];

            this.register_process_token(o, 'concat', '.', tc);

         }

         tokenized[tokenized.length] = chr;

         tokens[++tc] = [token, chr];

         this.register_process_token(o, token, chr, tc);

         beforeToken = token;

         beforeChar = chr;

      }

      console.log(' tokens: ', tokenized.join(''));

      if (c) {

         this.register_process_token(o, 'end', '', tokens.length);

      }


   },

   register_process_token: function(o, token, chr, index) {

      console.log(' token: ', token, 'chr: ', chr, ' index: ', index);

   }






});
